home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / ibus-table / engine / tabsqlitedb.py < prev    next >
Text File  |  2009-07-30  |  49KB  |  1,134 lines

  1. # -*- coding: utf-8 -*-
  2. # vim: set et sts=4 sw=4
  3. #
  4. # ibus-table - The Tables engine for IBus
  5. #
  6. # Copyright (c) 2008-2009 Yu Yuwei <acevery@gmail.com>
  7. #
  8. # This library is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU Lesser General Public
  10. # License as published by the Free Software Foundation; either
  11. # version 2.1 of the License, or (at your option) any later version.
  12. #
  13. # This library is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. # Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public
  19. # License along with this library; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  21. #
  22. # $Id: $
  23. #
  24.  
  25. import os
  26. import os.path as path
  27. from sys import stderr
  28. import sqlite3
  29. import tabdict
  30. import uuid
  31. import time
  32. import re
  33.  
  34. patt_r = re.compile(r'c([ea])(\d):(.*)')
  35. patt_p = re.compile(r'p(-{0,1}\d)(\d)')
  36.  
  37. # first make some number index we will used :)
  38. #(MLEN, CLEN, M0, M1, M2, M3, M4, PHRASE, FREQ, USER_FREQ) = range (0,10)
  39.  
  40.  
  41. class tabsqlitedb:
  42.     '''Phrase database for tables'''
  43.     def __init__(self, name = 'table.db', user_db = None, filename = None ):
  44.         # use filename when you are creating db from source
  45.         # use name when you are using db
  46.         # first we use the Parse in tabdict, which transform the char(a,b,c,...) to int(1,2,3,...) to fasten the sql enquiry
  47.         self.parse = tabdict.parse
  48.         self.deparse = tabdict.deparse
  49.         self._add_phrase_sqlstr = ''
  50.         self.old_phrases=[]
  51.         self.ime_property_cache = {}
  52.         
  53.         if filename:
  54.             # now we are creating db
  55.             self.db = sqlite3.connect( filename )
  56.         else:
  57.             try:
  58.                 os.system('cat %s > /dev/null' % name)
  59.             except:
  60.                 pass
  61.             # open system phrase db
  62.             self.db = sqlite3.connect(  name )
  63.         try:
  64.             self.db.execute( 'PRAGMA page_size = 8192; ' )
  65.             self.db.execute( 'PRAGMA cache_size = 20000; ' )
  66.             # increase the cache size to speedup sqlite enquiry
  67.             self.db.execute( 'PRAGMA temp_store = MEMORY; ' )
  68.             self.db.execute( 'PRAGMA synchronous = OFF; ' )
  69.         except:
  70.             print 'encountering error when init db'
  71.             pass
  72.         # create IME property table
  73.         self.db.executescript('CREATE TABLE IF NOT EXISTS main.ime (attr TEXT, val TEXT);')
  74.         # make sure we have values in ime table.
  75.         if not self.db.execute('SELECT val FROM main.ime \
  76.             WHERE attr="name";').fetchall():
  77.             ime_keys={'name':'',
  78.                       'name.zh_cn':'',
  79.                       'name.zh_hk':'',
  80.                       'name.zh_tw':'',
  81.                       'author':'somebody', 
  82.                       'uuid':'%s' % uuid.uuid4(),
  83.                       'serial_number':'%s' % time.strftime('%Y%m%d'),
  84.                       'icon':'ibus-table.svg',
  85.                       'license':'LGPL',
  86.                       'languages':'',
  87.                       'valid_input_chars':'abcdefghijklmnopqrstuvwxyz',
  88.                       'max_key_length':'4',
  89.             #          'commit_keys':'space',
  90.             #          'forward_keys':'Return',
  91.             #          'select_keys':'1,2,3,4,5,6,7,8,9,0',
  92.             #          'page_up_keys':'Page_Up,minus',
  93.             #          'page_down_keys':'Page_Down,equal',
  94.                       'status_prompt':'',
  95.                       'def_full_width_punct':'TRUE',
  96.                       'def_full_width_letter':'FALSE',
  97.                       'user_can_define_phrase':'FALSE',
  98.                       'pinyin_mode':'FALSE',
  99.                       'dynamic_adjust':'FALSE',
  100.                       'auto_commit':'false',
  101.                       #'no_check_chars':u'',
  102.                       'description':'A IME under IBus Table',
  103.                       'layout':'us',
  104.                       'rules':''}
  105.                       #'rules':'ce2:p11+p12+p21+p22;ce3:p11+p21+p22+p31;ca4:p11+p21+p31+p41'}
  106.             # inital the attribute in ime table, which should be updated from mabiao
  107.             for _name in ime_keys:
  108.                 sqlstr = 'INSERT INTO main.ime (attr,val) VALUES (?,?);'
  109.                 self.db.execute( sqlstr, (_name,ime_keys[_name]) )
  110.         # share variables in this class:
  111.         self._mlen = int ( self.get_ime_property ("max_key_length") )
  112.         # for chinese
  113.         self._is_chinese = self.is_chinese()
  114.         # for fast add word
  115.         self._set_add_phrase_sqlstr()
  116.         #(ID, MLEN, CLEN, M0, M1, M2, M3, M4, CATEGORY, PHRASE, FREQ, USER_FREQ) = range (0,12)
  117.         self._pt_index = ['id', 'mlen', 'clen']
  118.         for i in range(self._mlen):
  119.             self._pt_index.append ('m%d' %i)
  120.         if self._is_chinese:
  121.             self._pt_index += ['category']
  122.         self._pt_index += ['phrase','freq','user_freq']
  123.         self.user_can_define_phrase = self.get_ime_property('user_can_define_phrase')
  124.         if self.user_can_define_phrase:
  125.             if self.user_can_define_phrase.lower() == u'true' :
  126.                 self.user_can_define_phrase = True
  127.             else:
  128.                 self.user_can_define_phrase = False
  129.         else:
  130.             print 'Could not find "user_can_define_phrase" entry from database, is it a outdated database?'
  131.             self.user_can_define_phrase = False
  132.         
  133.         self.dynamic_adjust = self.get_ime_property('dynamic_adjust')
  134.         if self.dynamic_adjust:
  135.             if self.dynamic_adjust.lower() == u'true' :
  136.                 self.dynamic_adjust = True
  137.             else:
  138.                 self.dynamic_adjust = False
  139.         else:
  140.             print 'Could not find "dynamic_adjust" entry from database, is it a outdated database?'
  141.             self.dynamic_adjust = False
  142.         
  143.         self.rules = self.get_rules ()
  144.         self.pkeylens = []
  145.         if self.rules:
  146.             self.pkeylens = self.phrase_keys_len ()
  147.         
  148.         #self._no_check_chars = self.get_no_check_chars()
  149.         # for fast gouci
  150.         self._goucima={}
  151.         if filename:
  152.             # since we just creating db, we do not need userdb and mudb
  153.             return
  154.         
  155.         # user database:
  156.         if user_db != None:
  157.             home_path = os.getenv ("HOME")
  158.             tables_path = path.join (home_path, ".ibus",  "tables")
  159.             user_db = path.join (tables_path, user_db)
  160.             if not path.isdir (tables_path):
  161.                 os.makedirs (tables_path)
  162.             try:
  163.                 desc = self.get_database_desc (user_db)
  164.                 if desc == None :
  165.                     self.init_user_db (user_db)
  166.                 elif desc["version"] != "0.4":
  167.                     new_name = "%s.%d" %(user_db, os.getpid())
  168.                     print >> stderr, "Can not support the user db. We will rename it to %s" % new_name
  169.                     self.old_phrases = self.extra_user_phrases( user_db )
  170.                     os.rename (user_db, new_name)
  171.                     self.init_user_db (user_db)
  172.                 elif self.get_table_phrase_len(user_db) != len(self._pt_index):
  173.                     print >> stderr, "user db format outdated."
  174.                     # store old user phrases
  175.                     self.old_phrases = self.extra_user_phrases( user_db )
  176.                     new_name = "%s.%d" %(user_db, os.getpid())
  177.                     os.rename (user_db, new_name)
  178.                     self.init_user_db (user_db)
  179.             except:
  180.                 import traceback
  181.                 traceback.print_exc()
  182.         else:
  183.             user_db = ":memory:"
  184.         
  185.         # open user phrase database
  186.         try:
  187.             self.db.execute ('ATTACH DATABASE "%s" AS user_db;' % user_db)
  188.         except:
  189.             print >> stderr, "The user database was damaged. We will recreate it!"
  190.             os.rename (user_db, "%s.%d" % (user_db, os.getpid ()))
  191.             self.init_user_db (user_db)
  192.             self.db.execute ('ATTACH DATABASE "%s" AS user_db;' % user_db)
  193.         self.create_tables ("user_db")
  194.         if self.old_phrases:
  195.             # (mlen, phrase, freq, user_freq)
  196.             # the phrases will be deparse again, and then be added     
  197.             # the characters will be discard :(
  198.             #chars = filter (lambda x: x[0] == 1, self.old_phrases)
  199.             # print chars
  200.             phrases = filter (lambda x: x[0] > 1, self.old_phrases)
  201.             phrases = map(lambda x: [self.parse_phrase_to_tabkeys(x[1])]\
  202.                     + list(x[1:]) , phrases)
  203.  
  204.             map (self.u_add_phrase,phrases)
  205.             self.db.commit ()
  206.  
  207.  
  208.         # try create all tables in user database
  209.         self.create_indexes ("user_db",commit=False)
  210.         self.generate_userdb_desc ()
  211.         
  212.         # attach mudb for working process
  213.         mudb = ":memory:"  
  214.         self.db.execute ('ATTACH DATABASE "%s" AS mudb;' % mudb )
  215.         self.create_tables ("mudb")
  216.     
  217.     def update_phrase (self, entry, database='user_db'):
  218.         '''update phrase freqs'''
  219.         #print entry
  220.         _con = [ entry[-1] ] + list(entry[1:3+entry[1]]) + [entry[-3]]
  221.         #print _con
  222.         _condition = u''.join( map(lambda x: 'AND m%d = ? ' % x, range(entry[1]) )    )
  223.         #print _condition
  224.         sqlstr = 'UPDATE %s.phrases SET user_freq = ? WHERE mlen = ? AND clen = ? %s AND phrase = ?;' % (database, _condition)
  225.         #print sqlstr
  226.         self.db.execute ( sqlstr , _con )
  227.         # because we may update different db, we'd better commit every time.
  228.         self.db.commit()
  229.  
  230.     def sync_usrdb (self):
  231.         # we need to update the user_db
  232.         #print 'sync userdb'
  233.         mudata = self.db.execute ('SELECT * FROM mudb.phrases;').fetchall()
  234.         #print mudata
  235.         data_u = filter ( lambda x: x[-2] in [1,-3], mudata)
  236.         data_a = filter ( lambda x: x[-2]==2, mudata)
  237.         data_n = filter ( lambda x: x[-2]==-2, mudata)
  238.         #print data_a
  239.         data_a = map (lambda x: (u''.join ( map(self.deparse, x[3:3+x[1]])),x[-3],0,x[-1] ), data_a)
  240.         data_n = map (lambda x: (u''.join ( map(self.deparse, x[3:3+x[1]])),x[-3],-1,x[-1] ), data_n)
  241.         #print data_u
  242.         map (self.update_phrase, data_u)
  243.         #print self.db.execute('select * from user_db.phrases;').fetchall()
  244.         map (self.u_add_phrase,data_a)
  245.         map (self.u_add_phrase,data_n)
  246.         self.db.commit ()
  247.     
  248.     def is_chinese (self):
  249.         __lang = self.get_ime_property ('languages')
  250.         if __lang:
  251.             __langs = __lang.split(',')
  252.             for _l in __langs:
  253.                 if _l.lower().find('zh') != -1:
  254.                     return True
  255.         return False
  256.  
  257.  
  258.     def create_tables (self, database):
  259.         '''Create tables that contain all phrase'''
  260.  
  261.         try:
  262.             self.db.execute( 'PRAGMA cache_size = 20000; ' )
  263.             # increase the cache size to speedup sqlite enquiry
  264.         except:
  265.             pass
  266.         if database == 'main':
  267.             # create  ikeys table
  268.             sqlstr = 'CREATE TABLE IF NOT EXISTS %s.ikeys (ikey TEXT PRIMARY KEY, id INTEGER);' % database
  269.             self.db.execute ( sqlstr )
  270.         
  271.             # create goucima table, this table is used in construct new phrases
  272.             sqlstr = 'CREATE TABLE IF NOT EXISTS %s.goucima (zi TEXT PRIMARY KEY' % database
  273.             for i in range(self._mlen):
  274.                 sqlstr += ', g%d INTEGER' % i 
  275.             #sqlstr += ''.join(map (lambda x: ', g%d INTEGER' % x, range(self._mlen)) )
  276.             sqlstr += ');'
  277.             self.db.execute ( sqlstr )
  278.  
  279.             # create pinyin table, this table is used in search single character for user handly
  280.             sqlstr = 'CREATE TABLE IF NOT EXISTS %s.pinyin ( plen INTEGER, ' % database
  281.             #for i in range(6):
  282.             #    sqlstr += 'p%d INTEGER, ' % i 
  283.             sqlstr += ''.join( map (lambda x: 'p%d INTEGER, ' % x, range(7) ) )
  284.             sqlstr += 'zi TEXT, freq INTEGER);'
  285.             self.db.execute ( sqlstr )
  286.  
  287.         # create phrase table (mabiao)
  288.         sqlstr = 'CREATE TABLE IF NOT EXISTS %s.phrases (id INTEGER PRIMARY KEY AUTOINCREMENT,\
  289.                 mlen INTEGER, clen INTEGER, ' % database
  290.         #for i in range(self._mlen):
  291.         #    sqlstr += 'm%d INTEGER, ' % i 
  292.         sqlstr += ''.join ( map (lambda x: 'm%d INTEGER, ' % x, range(self._mlen)) )
  293.         if self._is_chinese:
  294.             sqlstr += 'category INTEGER, '
  295.         sqlstr += 'phrase TEXT, freq INTEGER, user_freq INTEGER);'
  296.         self.db.execute ( sqlstr )
  297.         self.db.commit()
  298.     
  299.     def update_ime (self, attrs):
  300.         '''Update attributes in ime table, attrs is a iterable object
  301.         Like [(attr,val), (attr,val), ...]
  302.         '''
  303.         sqlstr = 'UPDATE main.ime SET val = ? WHERE attr = ?;' 
  304.         for attr,val in attrs:
  305.             _sqlstr = 'SELECT * from main.ime WHERE attr = ?' 
  306.             res = self.db.execute( _sqlstr, (attr,) ).fetchall()
  307.             if res:
  308.                 self.db.execute(sqlstr,(val,attr))
  309.             else:
  310.                 #print '"',attr,'"'," didn't in ime property now!"
  311.                 pass
  312.         # then flush previous cache
  313.         self.ime_property_cache = {}
  314.         # we need to update some self variables now.
  315.         self._mlen = int (self.get_ime_property ('max_key_length' ))
  316.         self._is_chinese = self.is_chinese()
  317.         self._set_add_phrase_sqlstr()
  318.         self._pt_index = ['id', 'mlen', 'clen']
  319.         for i in range(self._mlen):
  320.             self._pt_index.append ('m%d' %i)
  321.         if self._is_chinese:
  322.             self._pt_index += ['category']
  323.         self._pt_index += ['phrase','freq','user_freq']
  324.         self.user_can_define_phrase = self.get_ime_property('user_can_define_phrase')
  325.         if self.user_can_define_phrase:
  326.             if self.user_can_define_phrase.lower() == u'true' :
  327.                 self.user_can_define_phrase = True
  328.             else:
  329.                 self.user_can_define_phrase = False
  330.         else:
  331.             print 'Could not find "user_can_define_phrase" entry from database, is it a outdated database?'
  332.             self.user_can_define_phrase = False
  333.         self.rules = self.get_rules ()
  334.  
  335.         self.db.commit()
  336.  
  337.     def get_rules (self):
  338.         '''Get phrase construct rules'''
  339.         rules={}
  340.         if self.user_can_define_phrase:
  341.             try:
  342.                 _rules = self.get_ime_property ('rules')
  343.                 if _rules:
  344.                     _rules = _rules.strip().split(';')
  345.                 for rule in _rules:
  346.                     res = patt_r.match (rule)
  347.                     if res:
  348.                         cms = []
  349.                         if res.group(1) == 'a':
  350.                             rules['above'] = int(res.group(2))
  351.                         _cms = res.group(3).split('+')
  352.                         if len(_cms) > int(self.get_ime_property('max_key_length')):
  353.                             print 'rule: "%s" over max key length' % rule
  354.                             break
  355.                         for _cm in _cms:
  356.                             cm_res = patt_p.match(_cm)
  357.                             cms.append(( int(cm_res.group(1)),int(cm_res.group(2)) ))
  358.                         rules[int(res.group(2))]=cms
  359.                     else:
  360.                         print 'not a legal rule: "%s"'  % rule 
  361.             except Exception:
  362.                 import traceback
  363.                 traceback.print_exc ()
  364.             return rules
  365.         else:
  366.             return ""
  367.  
  368.     def phrase_keys_len (self):
  369.         '''Return the phrase possible key length'''
  370.         max_len = self.rules["above"]
  371.         try:
  372.             return map ( lambda x: len(self.rules[x]), range(2, max_len+1) )[:]
  373.         except:
  374.             return None
  375.     
  376.     def get_no_check_chars (self):
  377.         '''Get the characters which engine should not change freq'''
  378.         _chars= self.get_ime_property('no_check_chars')
  379.         try:
  380.             _chars = _chars.decode('utf-8')
  381.         except:
  382.             pass
  383.         return _chars
  384.  
  385.     def add_phrases (self, phrases, database = 'main'):
  386.         '''Add phrases to database, phrases is a iterable object
  387.         Like: [(tabkeys, phrase, freq ,user_freq), (tabkeys, phrase, freq, user_freq), ...]
  388.         '''
  389.         map (self.add_phrase, phrases, [database]*len(phrases),[False]*len(phrases) )
  390.         self.db.commit()
  391.     
  392.     def add_new_phrases (self, nphrases, database='main'):
  393.         '''Add new phrases into db, new phrases is a object
  394.         of [(phrase,freq), (phrase,freq),...]'''
  395.         n_phrases=[]
  396.         for _ph, _freq in nphrases:
  397.             try:
  398.                 _tabkey = self.parse_phrase_to_tabkeys(_ph)
  399.                 if not self.check_phrase_internal (_ph, _tabkey, database):
  400.                     # we don't have this phrase
  401.                     n_phrases.append ( (_tabkey, _ph, _freq, 0) )
  402.             except:
  403.                 print '\"%s\" would not been added' % _ph
  404.         if n_phrases:
  405.             self.add_phrases ( n_phrases, database )
  406.     
  407.     def u_add_phrase (self,nphrase):
  408.         '''Add a phrase to userdb'''
  409.         self.add_phrase (nphrase,database='user_db',commit=False)
  410.  
  411.     def _set_add_phrase_sqlstr(self):
  412.         '''Create the sqlstr for add phrase according to self._mlen.'''
  413.         sqlstr = 'INSERT INTO %s.phrases ( mlen, clen, '
  414.         sql_suffix = 'VALUES ( ?, ?, '
  415.         mmlen = range(self._mlen)
  416.         sqlstr += ''.join ( map(lambda x: 'm%d, ' %x , mmlen) )
  417.         sql_suffix += ''.join ( map (lambda x: '?, ' , mmlen) )
  418.         if self._is_chinese:
  419.             sqlstr += 'category, '
  420.             sql_suffix += '?, '
  421.         sqlstr += 'phrase, freq, user_freq) '
  422.         sql_suffix += '?, ?, ? );'
  423.         sqlstr += sql_suffix
  424.         self._add_phrase_sqlstr = sqlstr
  425.  
  426.     def add_phrase (self, aphrase, database = 'main',commit=True):
  427.         '''Add phrase to database, phrase is a object of
  428.         (tabkeys, phrase, freq ,user_freq)
  429.         '''
  430.         sqlstr = self._add_phrase_sqlstr
  431.         try:
  432.             tabkeys,phrase,freq,user_freq = aphrase
  433.         except:
  434.             tabkeys,phrase,freq = aphrase
  435.             user_freq = 0
  436.         # now we will set the category bits if this is chinese
  437.         if self._is_chinese:
  438.             # this is the bitmask we will use,
  439.             # from low to high, 1st bit is simplify Chinese,
  440.             # 2nd bit is traditional Chinese,
  441.             # 3rd bit means out of gbk
  442.             category = 0
  443.             # make sure that we got a unicode string
  444.             if type(phrase) != type(u''):
  445.                 phrase = phrase.decode('utf8')
  446.             # first whether in gb2312
  447.             try:
  448.                 phrase.encode('gb2312')
  449.                 category |= 1
  450.             except:
  451.                 if 'πÇç'.decode('utf8') in phrase:
  452.                     # we add 'πÇç' into SC as well
  453.                     category |= 1
  454.             # second check big5-hkscs
  455.             try:
  456.                 phrase.encode('big5hkscs')
  457.                 category |= 1 << 1
  458.             except:
  459.                 # then check whether in gbk,
  460.                 if category & 1:
  461.                     # already know in SC
  462.                     pass
  463.                 else:
  464.                     # need to check
  465.                     try:
  466.                         phrase.encode('gbk')
  467.                         category |= 1
  468.                     except:
  469.                         # not in gbk
  470.                         pass
  471.             # then set for 3rd bit, if not in SC and TC
  472.             if not ( category & (1 | 1 << 1) ):
  473.                 category |= (1 << 2)
  474.         try:
  475.             tbks = self.parse(tabkeys)
  476.             if len(tbks) != len(tabkeys):
  477.                 print 'In %s %s: we parse tabkeys fail' % (phrase, tabkeys )
  478.                 return
  479.             record = [None] * (5 + self._mlen)
  480.             record [0] = len (tabkeys)
  481.             record [1] = len (phrase)
  482.             record [2: 2+len(tabkeys)] = map (lambda x: tbks[x].get_key_id(), range(0,len(tabkeys)))
  483.             if self._is_chinese:
  484.                 record +=[None]
  485.                 record[-4] = category
  486.             record[-3:] = phrase, freq, user_freq
  487.             self.db.execute (sqlstr % database, record)
  488.             if commit:
  489.                 self.db.commit()    
  490.         except Exception:
  491.             import traceback
  492.             traceback.print_exc()
  493.     
  494.     def add_goucima (self, gcms):
  495.         '''Add goucima into database, gcms is iterable object
  496.         Like gcms = [(zi,goucima),(zi,goucima), ...]
  497.         '''
  498.         count = 1
  499.         for zi,gcm in gcms:
  500.             _con = ''
  501.             _val = ''
  502.             _len = min ( len(gcm),self._mlen)
  503.             for i in range( _len ):
  504.                 _con += ', g%d' % i
  505.                 _val += ', ?' 
  506.             sqlstr = '''INSERT INTO main.goucima ( zi %s )
  507.             VALUES ( ? %s );''' % (_con, _val)
  508.             try:
  509.                 gc = self.parse(gcm)
  510.                 if len(gc) != len(gcm):
  511.                     error_m = u'%s %s: Can not parse goucima' % (zi, gcm )
  512.                     raise Exception ( error_m.encode ('utf8') )
  513.                 record = [zi]
  514.                 for i in range(_len):
  515.                     record.append( gc[i].get_key_id())
  516.                 self.db.execute (sqlstr , record)
  517.             
  518.             except Exception:
  519.                 import traceback
  520.                 traceback.print_exc()
  521.             count += 1
  522.         self.db.commit()
  523.     
  524.     def add_pinyin (self, pinyins, database = 'main'):
  525.         '''Add pinyin to database, pinyins is a iterable object
  526.         Like: [(zi,pinyin, freq), (zi, pinyin, freq), ...]
  527.         '''
  528.         sqlstr = 'INSERT INTO %s.pinyin ( plen, '
  529.         sql_suffix = 'VALUES ( ?, '
  530.         for i in range(7):
  531.             sqlstr += 'p%d, ' % i
  532.             sql_suffix += '?, '
  533.         sqlstr += 'zi, freq ) '
  534.         sql_suffix += '?, ? );'
  535.         sqlstr += sql_suffix
  536.         
  537.         count = 1
  538.         for pinyin,zi,freq in pinyins:
  539.             try:
  540.                 pinyin_n = pinyin.replace('1','!').replace('2','@').replace('3','#').replace('4','$').replace('5','%')
  541.                 py = self.parse(pinyin_n)
  542.                 if len(py) != len(pinyin_n):
  543.                     error_m = u'%s %s: Can not parse pinyin' % (zi, pinyin )
  544.                     raise Exception ( error_m.encode ('utf8') )
  545.                 record = [None]*10
  546.                 record [0] = len (pinyin_n)
  547.                 for i in range(0,len(pinyin_n)):
  548.                     record [ 1+i ] = py[i].get_key_id()
  549.                 record [-2] = zi
  550.                 record [-1] = freq
  551.                 self.db.execute (sqlstr % database, record)
  552.             except Exception:
  553.                 print count, ': ', zi.encode('utf8'), ' ', pinyin
  554.                 import traceback
  555.                 traceback.print_exc()
  556.             count += 1
  557.  
  558.         self.db.commit()    
  559.     
  560.     def optimize_database (self, database='main'):
  561.         sqlstr = '''
  562.             CREATE TABLE tmp AS SELECT * FROM %(database)s.phrases;
  563.             DELETE FROM %(database)s.phrases;
  564.             INSERT INTO %(database)s.phrases SELECT * FROM tmp ORDER BY
  565.             %(tabkeystr)s mlen ASC, user_freq DESC, freq DESC, id ASC;
  566.             DROP TABLE tmp;
  567.             CREATE TABLE tmp AS SELECT * FROM %(database)s.goucima;
  568.             DELETE FROM %(database)s.goucima;
  569.             INSERT INTO %(database)s.goucima SELECT * FROM tmp ORDER BY zi,g0,g1;
  570.             DROP TABLE tmp;
  571.             CREATE TABLE tmp AS SELECT * FROM %(database)s.pinyin;
  572.             DELETE FROM %(database)s.pinyin;
  573.             INSERT INTO %(database)s.pinyin SELECT * FROM tmp ORDER BY p0,p1,p2,p3,p4,p5,plen ASC;
  574.             DROP TABLE tmp;
  575.             '''
  576.         tabkeystr = ''
  577.         for i in range(self._mlen):
  578.             tabkeystr +='m%d, ' % i
  579.         self.db.executescript (sqlstr % {'database':database,'tabkeystr':tabkeystr })
  580.         self.db.executescript ("VACUUM;")
  581.         self.db.commit()
  582.     
  583.     def drop_indexes(self, database):
  584.         '''Drop the index in database to reduce it's size'''
  585.         sqlstr = '''
  586.             DROP INDEX IF EXISTS %(database)s.goucima_index_z;
  587.             DROP INDEX IF EXISTS %(database)s.pinyin_index_i;
  588.             DROP INDEX IF EXISTS %(database)s.phrases_index_p;
  589.             DROP INDEX IF EXISTS %(database)s.phrases_index_i;
  590.             VACUUM; 
  591.             ''' % { 'database':database }
  592.         
  593.         self.db.executescript (sqlstr)
  594.         self.db.commit()
  595.     
  596.     def create_indexes(self, database, commit=True):
  597.         sqlstr = '''
  598.             DROP INDEX IF EXISTS %(database)s.goucima_index_z;
  599.             CREATE INDEX IF NOT EXISTS %(database)s.goucima_index_z ON goucima (zi);
  600.             DROP INDEX IF EXISTS %(database)s.pinyin_index_i;
  601.             CREATE INDEX IF NOT EXISTS %(database)s.pinyin_index_i ON pinyin (p0,p1,p2,p3,p4,p5,plen ASC, freq DESC);
  602.             VACUUM; 
  603.             ''' % { 'database':database }
  604.  
  605.         sqlstr_t = '''
  606.             DROP INDEX IF EXISTS %(database)s.phrases_index_p;
  607.             CREATE INDEX IF NOT EXISTS %(database)s.phrases_index_p ON phrases
  608.             (%(tabkeystr)s mlen ASC, freq DESC, id ASC);
  609.             DROP INDEX IF EXISTS %(database)s.phrases_index_i;
  610.             CREATE INDEX IF NOT EXISTS %(database)s.phrases_index_i ON phrases (phrase, mlen ASC);
  611.             ''' 
  612.         tabkeystr = ''
  613.         for i in range(self._mlen):
  614.             tabkeystr +='m%d,' % i
  615.         if database == 'main':
  616.             sqlstr = sqlstr_t % {'database':database,'tabkeystr':tabkeystr } + sqlstr
  617.         else:
  618.             sqlstr = sqlstr_t % {'database':database,'tabkeystr':tabkeystr }
  619.         self.db.executescript (sqlstr)
  620.         if commit:
  621.             self.db.commit()
  622.     
  623.     def compare (self,x,y):
  624.         return cmp (x[1], y[1]) or -(cmp (x[-1], y[-1])) \
  625.                 or -(cmp (x[-2], y[-2])) or (cmp (x[0], y[0]))
  626.  
  627.     def select_words( self, tabkeys, onechar=False, bitmask=0 ):
  628.         '''
  629.         Get phrases from database by tab_key objects
  630.         ( which should be equal or less than the max key length)
  631.         This method is called in table.py by passing UserInput held data
  632.         Return result[:] 
  633.         '''
  634.         # firstly, we make sure the len we used is equal or less than the max key length
  635.         _len = min( len(tabkeys),self._mlen )
  636.         _condition = ''
  637.         _condition += ''.join ( map (lambda x: 'AND m%d = ? ' %x, range(_len) ) )
  638.         if onechar:
  639.             # for some users really like to select only single characters
  640.             _condition += 'AND clen=1 '
  641.         if bitmask:
  642.             # now just the bits for chinese
  643.             all_ints = xrange(1,5)
  644.             need_ints = filter (lambda x: x & bitmask, all_ints)
  645.             bit_condition = 'OR'.join( map(lambda x: ' category = %d ' %x,\
  646.                     need_ints) )
  647.             _condition += 'AND (%s) ' % bit_condition
  648.  
  649.         # you can increase the x in _len + x to include more result, but in the most case, we only need one more key result, so we don't need the extra overhead :)
  650.         # we start search for 1 key more, if nothing, then 2 key more and so on
  651.         # this is the max len we need to add into the select cause.
  652.         w_len = self._mlen - _len +1
  653.         # we start from 2, because it is < in the sqlite select, which need 1 more.
  654.         x_len = 2
  655.         while x_len <= w_len + 1:
  656.             sqlstr = '''SELECT * FROM (SELECT * FROM main.phrases WHERE mlen < %(mk)d  %(condition)s 
  657.             UNION ALL
  658.             SELECT * FROM user_db.phrases WHERE mlen < %(mk)d %(condition)s 
  659.             UNION ALL
  660.             SELECT * FROM mudb.phrases WHERE mlen < %(mk)d %(condition)s )
  661.             ORDER BY mlen ASC, user_freq DESC, freq DESC, id ASC;''' % { 'mk':_len+x_len, 'condition':_condition}
  662.             # we have redefine the __int__(self) in class tabdict.tab_key to return the key id, so we can use map to got key id :)
  663.             _tabkeys = map(int,tabkeys[:_len])
  664.             _tabkeys += _tabkeys + _tabkeys
  665.             result = self.db.execute(sqlstr, _tabkeys).fetchall()
  666.             #self.db.commit()
  667.             # if we find word, we stop this while, 
  668.             if len(result) >0:
  669.                 break
  670.             x_len += 1
  671.         # here in order to get high speed, I use complicated map
  672.         # to subtitute for
  673.         sysdb={}
  674.         usrdb={}
  675.         mudb={}
  676.         _cand = []
  677.         #searchres = map ( lambda res: res[-2] and [ True, [(res[:-2],[res[:-1],res[-1:]])] ]\
  678.         #        or [ False, [(res[:-2] , [res[:-1],res[-1:]])] ] \
  679.         #        , result )
  680.         searchres = map ( lambda res: [ int(res[-2]), int(res[-1]),
  681.             [(res[1:-2],[res[:-1],res[-1:]])] ], result)
  682.         # for sysdb
  683.         reslist=filter( lambda x: not x[1], searchres )
  684.         map (lambda x: sysdb.update(x[2]), reslist)
  685.         # for usrdb
  686.         reslist=filter( lambda x: ( x[0] in [0,-1] ) and x[1], searchres )
  687.         map (lambda x: usrdb.update(x[2]), reslist)
  688.         # for mudb
  689.         reslist=filter( lambda x: ( x[0] not in [0,-1] ) and x[1], searchres )
  690.         map (lambda x: mudb.update(x[2]), reslist)
  691.  
  692.         # first process mudb
  693.         searchres = map ( lambda key: mudb[key][0] + mudb[key][1], mudb )
  694.         #print searchres
  695.         map (_cand.append, searchres)
  696.  
  697.         # now process usrdb and sysdb
  698.         searchres = map ( lambda key:  (not mudb.has_key(key))  and usrdb[key][0] + usrdb[key][1]\
  699.                 or None , usrdb )
  700.         searchres = filter(lambda x: bool(x), searchres )
  701.         #print searchres
  702.         map (_cand.append, searchres)
  703.         searchres = map ( lambda key: ((not mudb.has_key(key)) and (not usrdb.has_key(key)) )and sysdb[key][0] + sysdb[key][1]\
  704.                 or None, sysdb )
  705.         searchres = filter (lambda x: bool(x), searchres)
  706.         map (_cand.append, searchres)
  707.         #for key in usrdb:
  708.         #    if not sysdb.has_key (key):
  709.         #        _cand.append( usrdb[key][0] + usrdb[key][1] )
  710.         #    else:
  711.         #        _cand.append( sysdb[key][0] + usrdb[key][1] )
  712.         #for key in sysdb:
  713.         #    if not usrdb.has_key (key):
  714.         #        _cand.append( sysdb[key][0] + sysdb[key][1] )
  715.         _cand.sort(cmp=self.compare)
  716.         return _cand[:]
  717.  
  718.     def select_zi( self, tabkeys ):
  719.         '''
  720.         Get zi from database by tab_key objects
  721.         ( which should be equal or less than 6)
  722.         This method is called in table.py by passing UserInput held data
  723.         Return  result[:] 
  724.         '''
  725.         # firstly, we make sure the len we used is equal or less than
  726.         # the max pinyin length 7 (include tune[1-5])
  727.         _len = min( len(tabkeys), 7 )
  728.         _condition = ''
  729.         #for i in range(_len):
  730.         #    _condition += 'AND p%d = ? ' % i
  731.         _condition += ''.join ( map (lambda x: 'AND p%d = ? ' %x, range(_len)) )
  732.         # you can increase the x in _len + x to include more result, but in the most case, we only need one more key result, so we don't need the extra overhead :)
  733.         # here we need make sure that the 3 <= plen <=7,
  734.         # so , if if _len < 3, than we start from 3; 
  735.         #   if _len >= 3, we start from _len + 1;
  736.         #   if _len = 7, we start from _len
  737.         if _len < 7:
  738.             if _len < 3:
  739.                 x_len = 3
  740.             else:
  741.                 x_len = _len + 1
  742.         else:
  743.             x_len = _len
  744.         
  745.         while x_len < 8:
  746.             sqlstr = '''SELECT * FROM main.pinyin WHERE plen < %(mk)d  %(condition)s 
  747.                 ORDER BY plen ASC, freq DESC;''' % { 'mk':x_len, 'condition':_condition}
  748.             # we have redefine the __int__(self) in class tabdict.tab_key to return the key id, so we can use map to got key id :)
  749.             _tabkeys = map(int,tabkeys[:_len])
  750.             result = self.db.execute(sqlstr, _tabkeys).fetchall()
  751.             if len(result) > 0:
  752.                 break
  753.             x_len += 1
  754.         #self.db.commit()
  755.         return result[:]
  756.  
  757.     def get_ime_property( self, attr ):
  758.         '''get IME property from database, attr is the string of property,
  759.         which should be str.lower() :)
  760.         '''
  761.         if not attr in self.ime_property_cache:
  762.             sqlstr = 'SELECT val FROM main.ime WHERE attr = ?' 
  763.             _result = self.db.execute( sqlstr, (attr,)).fetchall()
  764.             if _result:
  765.                 self.ime_property_cache[attr] = _result[0][0]
  766.             else:
  767.                 self.ime_property_cache[attr] = None
  768.         return self.ime_property_cache[attr]
  769.  
  770.     def get_phrase_table_index (self):
  771.         '''get a list of phrase table columns name'''
  772.         return self._pt_index[:]
  773.  
  774.     def generate_userdb_desc (self):
  775.         try:
  776.             sqlstring = 'CREATE TABLE IF NOT EXISTS user_db.desc (name PRIMARY KEY, value);'
  777.             self.db.executescript (sqlstring)
  778.             sqlstring = 'INSERT OR IGNORE INTO user_db.desc  VALUES (?, ?);'
  779.             self.db.execute (sqlstring, ('version', '0.4'))
  780.             sqlstring = 'INSERT OR IGNORE INTO user_db.desc  VALUES (?, DATETIME("now", "localtime"));'
  781.             self.db.execute (sqlstring, ("create-time", ))
  782.             self.db.commit ()
  783.         except:
  784.             import traceback
  785.             traceback.print_exc ()
  786.  
  787.     def init_user_db (self,db_file):
  788.         if not path.exists (db_file):
  789.             db = sqlite3.connect (db_file)
  790.             db.execute('PRAGMA page_size = 4096;')
  791.             db.execute( 'PRAGMA cache_size = 20000;' )
  792.             db.execute( 'PRAGMA temp_store = MEMORY; ' )
  793.             db.execute( 'PRAGMA synchronous = OFF; ' )
  794.             db.commit()
  795.     
  796.     def get_database_desc (self, db_file):
  797.         if not path.exists (db_file):
  798.             return None
  799.         try:
  800.             db = sqlite3.connect (db_file)
  801.             db.execute('PRAGMA page_size = 4096;')
  802.             db.execute( 'PRAGMA cache_size = 20000;' )
  803.             db.execute( 'PRAGMA temp_store = MEMORY; ' )
  804.             db.execute( 'PRAGMA synchronous = OFF; ' )
  805.             desc = {}
  806.             for row in db.execute ("SELECT * FROM desc;").fetchall():
  807.                 desc [row[0]] = row[1]
  808.             self.db.commit()
  809.             return desc
  810.         except:
  811.             return None
  812.  
  813.     def get_table_phrase_len(self, db_file):
  814.         table_patt = re.compile(r'.*\((.*)\)')
  815.         if not path.exists (db_file):
  816.             return 0
  817.         try:
  818.             db = sqlite3.connect (db_file)
  819.             tp_res = db.execute("select sql from sqlite_master\
  820.                     where name='phrases';").fetchall()
  821.             self.db.commit()
  822.             res = table_patt.match(tp_res[0][0])
  823.             if res:
  824.                 tp = res.group(1).split(',')
  825.                 return len(tp)
  826.             else:
  827.                 return 0
  828.         except:
  829.             return 0
  830.     
  831.     def cache_goucima (self):
  832.         self._goucima = {}
  833.         goucima = self.db.execute('SELECT * FROM main.goucima;').fetchall()
  834.         map(lambda x: self._goucima.update({x[0]:x[1:]}), goucima)
  835.     
  836.     def get_gcm_id (self, zi):
  837.         '''Get goucima of given character'''
  838.         if self._goucima:
  839.             # we already cache the goucima
  840.             if not isinstance(zi,unicode):
  841.                 zi = zi.decode('utf-8')
  842.             try:
  843.                 gcds = self._goucima[zi]
  844.                 return gcds
  845.             except:
  846.                 pass
  847.         sqlstr = 'SELECT %s FROM main.goucima WHERE zi =?;' % ','.join( map (lambda x: 'g%d' % x, range(self._mlen) ) )
  848.         return self.db.execute(sqlstr,(zi,)).fetchall()[0]
  849.  
  850.     def parse_phrase (self, phrase):
  851.         '''Parse phrase to get its Table code'''
  852.         # first we make sure that we are parsing unicode string
  853.         try:
  854.             phrase = unicode(phrase)
  855.         except:
  856.             phrase = phrase.decode('utf8')
  857.         p_len = len(phrase) 
  858.         tabkeylist = []
  859.         if p_len < 2:
  860.             # phrase should not be shorter than 2
  861.             return []
  862.         try:
  863.             if p_len >= self.rules['above']:
  864.                 rule = self.rules[ self.rules['above'] ]
  865.             elif p_len in self.rules:
  866.                 rule = self.rules[p_len]
  867.             else:
  868.                 raise Exception ('unsupport len of phrase')
  869.             if len(rule) > self._mlen:
  870.                 raise Exception ('fault rule: %s' % rule)
  871.             #for (zi,ma) in rule:
  872.             #    if zi > 0:
  873.             #        zi -= 1
  874.             #    gcm = self.get_gcm_id (phrase[zi])
  875.             #    tabkeylist.append(gcm[ma-1])
  876.             tabkeylist = map (lambda x: self.get_gcm_id ( phrase[x[0]-1] )[ x[1]-1 ], rule )
  877.             return [len( tabkeylist)] + [p_len]  + tabkeylist[:] + [phrase]
  878.  
  879.         except:
  880.             print "pharse pharse \"%s\" fail." % phrase.encode("utf-8")
  881.             #import traceback
  882.             #traceback.print_exc ()
  883.  
  884.     def parse_phrase_to_tabkeys (self,phrase):
  885.         '''Get the Table encoding of the phrase in string form'''
  886.         try:
  887.             tabres = self.parse_phrase (phrase) [2:-1]
  888.         except:
  889.             tabres = None
  890.         if tabres:
  891.             tabkeys= u''.join ( map(self.deparse, tabres) )
  892.         else:
  893.             tabkeys= u''
  894.         return tabkeys
  895.  
  896.     def check_phrase (self,phrase,tabkey=None,database='main'):
  897.         # if IME didn't support user define phrase,
  898.         # we divide user input phrase into characters,
  899.         # and then check its frequence
  900.         if type(phrase) != type(u''):
  901.             phrase = phrase.decode('utf8')
  902.         if self.user_can_define_phrase:
  903.             self.check_phrase_internal (phrase, tabkey, database)
  904.         else:
  905.             map(self.check_phrase_internal, phrase)
  906.     
  907.     def check_phrase_internal (self,phrase,tabkey=None,database='main'):
  908.         '''Check word freq and user_freq
  909.         '''
  910.         if type(phrase) != type(u''):
  911.             phrase = phrase.decode('utf8')
  912.         if self._is_chinese:
  913.             if phrase in tabdict.chinese_nocheck_chars:
  914.                 return
  915.         if len(phrase) >=2:
  916.             try:
  917.                 wordattr = self.parse_phrase ( phrase )
  918.                 _len = len (wordattr) -3
  919.             except:
  920.                 # if we don't have goucima:
  921.                 return
  922.         if (not tabkey) or len(tabkey) > self._mlen :
  923.             sqlstr = '''SELECT * FROM (SELECT * FROM main.phrases WHERE phrase = ?
  924.             UNION ALL SELECT * FROM user_db.phrases WHERE phrase = ?
  925.             UNION ALL SELECT * FROM mudb.phrases WHERE phrase = ?)
  926.             ORDER BY user_freq DESC, freq DESC, id ASC;
  927.             ''' 
  928.             result = self.db.execute(sqlstr, (phrase,phrase,phrase)).fetchall()
  929.         else:
  930.             # we are using this to check whether the tab-key and phrase is in db
  931.             #print "tabkey: ", tabkey
  932.             tabks = self.parse (tabkey)
  933.             #print "tabks: ", tabks
  934.             tabkids = tuple( map(int,tabks) )
  935.             condition = ' and '.join( map(lambda x: 'm%d = ?' % x, range( len(tabks) )) )
  936.             sqlstr = '''SELECT * FROM 
  937.             (
  938.                 SELECT * FROM main.phrases WHERE phrase = ? and %(cond)s
  939.                 UNION ALL SELECT * FROM user_db.phrases WHERE phrase = ? and %(cond)s
  940.                 UNION ALL SELECT * FROM mudb.phrases WHERE phrase = ? and %(cond)s
  941.             )
  942.             ORDER BY user_freq DESC, freq DESC, id ASC;
  943.             ''' % {'cond':condition}
  944.             #print sqlstr
  945.             result = self.db.execute(sqlstr, ((phrase,)+tabkids)*3 ).fetchall()
  946.             if not bool(result):
  947.                 sqlstr = '''SELECT * FROM (SELECT * FROM main.phrases WHERE phrase = ?
  948.                 UNION ALL SELECT * FROM user_db.phrases WHERE phrase = ?
  949.                 UNION ALL SELECT * FROM mudb.phrases WHERE phrase = ?)
  950.                 ORDER BY user_freq DESC, freq DESC, id ASC;
  951.                 ''' 
  952.                 result = self.db.execute(sqlstr, (phrase,phrase,phrase)).fetchall()
  953.  
  954.         sysdb = {}
  955.         usrdb = {}
  956.         mudb = {}
  957.         #print "result is: ", result 
  958.         searchres = map ( lambda res: [ int(res[-2]), int(res[-1]),
  959.             [(res[1:-2],[res[:-1],res[-1]])] ], result)
  960.         # for sysdb
  961.         reslist=filter( lambda x: not x[1], searchres )
  962.         map (lambda x: sysdb.update(x[2]), reslist)
  963.         #print "sysdb is ", sysdb
  964.         # for usrdb
  965.         reslist=filter( lambda x: ( x[0] in [0,-1] ) and x[1], searchres )
  966.         map (lambda x: usrdb.update(x[2]), reslist)
  967.         #print "usrdb is ", usrdb 
  968.         # for mudb
  969.         reslist=filter( lambda x: (x[0] not in [0,-1])  and x[1], searchres )
  970.         map (lambda x: mudb.update(x[2]), reslist)
  971.         #print "mudb is ", mudb
  972.         
  973.         tabkey = ''
  974.         if len(phrase) >=2:
  975.             tabkey = u''.join ( map(self.deparse,wordattr[2:2+_len]) )
  976.             #for k in wordattr[2:2+_len]:
  977.             #    tabkey += self.deparse (k)
  978.         
  979.         if self._is_chinese:
  980.             sqlstr = 'UPDATE mudb.phrases SET user_freq = ? WHERE mlen = ? AND clen = ? %s AND category = ? AND phrase = ?;'
  981.         else:
  982.             sqlstr = 'UPDATE mudb.phrases SET user_freq = ? WHERE mlen = ? AND clen = ? %s AND phrase = ?;'
  983.         
  984.         try:
  985.             if len(phrase) == 1:
  986.                 # this is a character
  987.                 if not self.dynamic_adjust:
  988.                     # we should change the frequency of words
  989.                     return
  990.                 # we remove the keys contained in mudb from usrdb
  991.                 keyout = filter (lambda k: mudb.has_key(k), usrdb.keys() )
  992.                 map (usrdb.pop, keyout)
  993.                 # we remove the keys contained in mudb and usrdb from sysdb
  994.                 keyout = filter (lambda k: mudb.has_key(k) or usrdb.has_key(k) , sysdb.keys() )
  995.                 map (sysdb.pop, keyout)
  996.                 # first mudb
  997.                 map (lambda res: self.db.execute ( sqlstr % ''.join( map(lambda x: 'AND m%d = ? ' % x, range(res[0])) ) ,  [ mudb[res][1] + 1 ] + list( res[:2+res[0]]) + list (res[2+self._mlen:]) ) , mudb.keys())
  998.                 self.db.commit()
  999.                 # -----original for loop of above map: 
  1000.                 #for res in mudb.keys ():
  1001.                 #    _con = [ mudb[res][1] + 1 ] + list( res[:2+res[0]]) + list (res[2+self._mlen:])
  1002.                 #    _condition = ''.join( map(lambda x: 'AND m%d = ? ' % x, range(res[0])) )    
  1003.                 #    self.db.execute ( sqlstr % _condition, _con )
  1004.                 
  1005.                 # then usrdb
  1006.                 map ( lambda res: self.add_phrase ( (''.join ( map(self.deparse,res[2:2+int(res[0])] ) ),phrase,1,usrdb[res][1]+1  ), database = 'mudb') , usrdb.keys() )                
  1007.                 # -----original for loop of above map: 
  1008.                 #for res in usrdb.keys ():
  1009.                 #    #if mudb.has_key (res):
  1010.                 #    #    continue
  1011.                 #    tabkey = ''.join ( map(self.deparse,res[2:2+int(res[0])] ) )
  1012.                 #    # here we use freq 1 to denote the phrase needed update in user_db
  1013.                 #    self.add_phrase ((tabkey,phrase,1,usrdb[res][1]+1 ), database = 'mudb')
  1014.                 # last sysdb
  1015.                 map ( lambda res: self.add_phrase ( ( ''.join ( map(self.deparse,res[2:2+int(res[0])]) ),phrase,2,1 ), database = 'mudb'), sysdb.keys() )
  1016.                 # -----original for loop of above map: 
  1017.                 #for res in sysdb.keys ():
  1018.                 #    tabkey = ''.join ( map(self.deparse,res[2:2+int(res[0])]) )
  1019.                 #    # here we use freq 2 to denote the word needed addition to user_db
  1020.                 #    self.add_phrase ((tabkey,phrase,2,1), database = 'mudb')
  1021.             else:
  1022.                 # this is a phrase
  1023.                 if len (result) == 0 and self.user_can_define_phrase:
  1024.                     # this is a new phrase, we add it into user_db
  1025.                     self.add_phrase ( (tabkey,phrase,-2,1), database = 'mudb')
  1026.                 elif len (result) > 0:
  1027.                     if not self.dynamic_adjust:
  1028.                         # we should change the frequency of words
  1029.                         return
  1030.                     # we remove the keys contained in mudb from usrdb
  1031.                     keyout = filter (lambda k: mudb.has_key(k), usrdb.keys() )
  1032.                     map (usrdb.pop, keyout)
  1033.                     # we remove the keys contained in mudb and usrdb from sysdb
  1034.                     keyout = filter (lambda k: mudb.has_key(k) or usrdb.has_key(k) , sysdb.keys() )
  1035.                     map (sysdb.pop, keyout)
  1036.                     
  1037.                     # first we process mudb
  1038.                     # the original for loop can be found above in 'len==1'
  1039.                     map (lambda res: self.db.execute ( sqlstr % ''.join( map(lambda x: 'AND m%d = ? ' % x, range(res[0])) ) ,  [ mudb[res][1] + 1 ] + list( res[:2+res[0]]) + list (res[2+self._mlen:]) ) , mudb.keys())
  1040.                     self.db.commit()
  1041.                     # then usrdb
  1042.                     map ( lambda res: self.add_phrase ( (''.join ( map(self.deparse,res[2:2+int(res[0])] ) ),phrase,(-3 if usrdb[res][0][-1] == -1 else 1),usrdb[res][1]+1  ), database = 'mudb') , usrdb.keys() )                
  1043.                     # last sysdb
  1044.                     map ( lambda res: self.add_phrase ( ( ''.join ( map(self.deparse,res[2:2+int(res[0])]) ),phrase,2,1 ), database = 'mudb'), sysdb.keys() )
  1045.  
  1046.                 else:
  1047.                     # we come to here when the ime dosen't support user phrase define
  1048.                     pass
  1049.             
  1050.             #self.db.commit()
  1051.         except:
  1052.             import traceback
  1053.             traceback.print_exc ()
  1054.  
  1055.     def find_zi_code (self,zi):
  1056.         '''Check word freq and user_freq
  1057.         '''
  1058.         zi = zi.decode('utf8')
  1059.         sqlstr = '''SELECT * FROM main.phrases WHERE phrase = ?
  1060.         ORDER BY mlen ASC;
  1061. ''' 
  1062.         result = self.db.execute(sqlstr, (zi,)).fetchall()
  1063.         #self.db.commit()
  1064.         codes = []
  1065.         try:
  1066.             if result:
  1067.                 for _res in result:
  1068.                     tabkey = u''
  1069.                     for i in range ( int ( _res[1] ) ):
  1070.                         tabkey += self.deparse ( _res[3+i] )
  1071.                     codes.append(tabkey)
  1072.         except:
  1073.             import traceback
  1074.             traceback.print_exc ()
  1075.         return codes[:]
  1076.  
  1077.     def remove_phrase (self,phrase,database='user_db'):
  1078.         '''Remove phrase from database, default is from user_db
  1079.         phrase should be the a row of select * result from database
  1080.         Like (id, mlen,clen,m0,m1,m2,m3,phrase,freq,user_freq)
  1081.         '''
  1082.         _ph = list(phrase[:-2])
  1083.         _condition = ''    
  1084.         for i in range(_ph[1]):
  1085.             _condition += 'AND m%d = ? ' % i
  1086.         nn =_ph.count(None)
  1087.         if nn:
  1088.             for i in range(nn):
  1089.                 _ph.remove(None)
  1090.         if self._is_chinese:
  1091.             msqlstr= 'SELECT * FROM %(database)s.phrases WHERE mlen = ? and clen = ? %(condition)s AND category = ? AND phrase = ? ;' % { 'database':database, 'condition':_condition }
  1092.         else:
  1093.             msqlstr= 'SELECT * FROM %(database)s.phrases WHERE mlen = ? and clen = ? %(condition)s AND phrase = ? ;' % { 'database':database, 'condition':_condition }
  1094.         if self.db.execute(msqlstr, _ph[1:]).fetchall():
  1095.             if self._is_chinese:
  1096.                 sqlstr = 'DELETE FROM %(database)s.phrases WHERE mlen = ? AND clen =? %(condition)s AND category = ? AND phrase = ?  ;' % { 'database':database, 'condition':_condition }
  1097.             else:
  1098.                 sqlstr = 'DELETE FROM %(database)s.phrases WHERE mlen = ? AND clen =? %(condition)s AND phrase = ?  ;' % { 'database':database, 'condition':_condition }
  1099.             self.db.execute(sqlstr,_ph[1:])
  1100.             self.db.commit()
  1101.  
  1102.         if self._is_chinese:
  1103.             msqlstr= 'SELECT * FROM mudb.phrases WHERE mlen = ? and clen = ? %(condition)s AND category = ? AND phrase = ? ;' % { 'condition':_condition }
  1104.         else:
  1105.             msqlstr= 'SELECT * FROM mudb.phrases WHERE mlen = ? and clen = ? %(condition)s AND phrase = ? ;' % { 'condition':_condition }
  1106.         if self.db.execute(msqlstr, _ph[1:]).fetchall():
  1107.             if self._is_chinese:
  1108.                 sqlstr = 'DELETE FROM mudb.phrases WHERE mlen = ? AND clen =? %(condition)s AND category = ? AND phrase = ?  ;' % {  'condition':_condition }
  1109.             else:
  1110.                 sqlstr = 'DELETE FROM mudb.phrases WHERE mlen = ? AND clen =? %(condition)s AND phrase = ?  ;' % {  'condition':_condition }
  1111.             self.db.execute(sqlstr,_ph[1:])
  1112.             self.db.commit()
  1113.  
  1114.     def extra_user_phrases(self, udb, only_defined=False):
  1115.         '''extract user phrases from database'''
  1116.         try:
  1117.             db = sqlite3.connect(udb)
  1118.         except:
  1119.             return None
  1120.         if only_defined:
  1121.             _phrases = db.execute(\
  1122.                     "SELECT clen, phrase, freq, sum(user_freq)\
  1123.                     FROM phrases \
  1124.                     WHERE freq=-1 AND mlen != 0 \
  1125.                     GROUP BY clen,phrase;").fetchall()
  1126.         else:
  1127.             _phrases = db.execute(\
  1128.                     "SELECT clen, phrase, freq, sum(user_freq)\
  1129.                     FROM phrases\
  1130.                     WHERE mlen !=0 \
  1131.                     GROUP BY clen,phrase;").fetchall()
  1132.         db.commit()
  1133.         return _phrases[:]
  1134.